use std::{convert::TryFrom, hash::{Hash, Hasher}, sync::Arc, vec};

use iced::{Subscription, futures::{self, stream::BoxStream}};
use iced_native::subscription::Recipe;

use std::net::UdpSocket;

pub fn recv_msg(id: u32, socket: Arc<UdpSocket>) -> Subscription<Option<moyu::Packet>> {
    let recv = Receiver {
        id,
        socket,
    };

    Subscription::from_recipe(recv)
}

pub struct Receiver {
    id: u32,
    socket: Arc<UdpSocket>,
}

impl<H, E> Recipe<H, E> for Receiver 
    where H: Hasher {
    type Output = Option<moyu::Packet>;

    fn hash(&self, state: &mut H) {
        self.id.hash(state)
    }

    fn stream(
        self: Box<Self>,
        _input: BoxStream<E>,
    ) -> BoxStream<Self::Output> {
        let socket = self.socket.try_clone().unwrap();
        socket.set_nonblocking(true).unwrap();
        let buffer = vec![0u8; 1024];

        Box::pin(futures::stream::unfold(
            (tokio::net::UdpSocket::from_std(socket).unwrap(), buffer),
            move |state| async move {
                let (socket, mut buf) = state;
                
                if let Ok(len) = socket.recv(&mut buf).await {
                    Some((moyu::Packet::try_from(&buf[..len]).ok(), (socket, buf)))
                } else {
                    Some((None, (socket, buf)))
                }
            }
        ))
    }
}